//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2013 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_AUTO_UTILITY_H_
#define _BUOLA_AUTO_UTILITY_H_

#include <buola/auto/exceptions.h>
#include <buola/auto/intrusive_ptr.h>
#include <buola/auto/defines.h>
#include <buola/auto/string.h>
#include <buola/auto/threads.h>
#include <buola/auto/traits.h>

#include <vector>

namespace buola {

///\addtogroup utility
///@{

///\name Names
//@{

DECLARE_NAME(Undefined)
DECLARE_NAME(Empty)
DECLARE_NAME(Zero)
DECLARE_NAME(DeleteEmpty)
DECLARE_NAME(Default)
DECLARE_NAME(All)
DECLARE_NAME(Required)
DECLARE_NAME(Flush)

DECLARE_NAME(InputOutput)
DECLARE_DERIVED_NAME(Input,NInputOutput)
DECLARE_DERIVED_NAME(Output,NInputOutput)

///used as an integer parameter to some templates, to indicate that the size is not known at compile time
static const size_t cDynamic=(size_t)-1;
static const size_t cUnknown=(size_t)-2;

enum class EEndianness
{
    LITTLE=LITTLE_ENDIAN,
    BIG=BIG_ENDIAN,
    SYSTEM=BYTE_ORDER
};

//@}

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// make the object uncopiable
///
/// Inheriting from \c UUncopiable disables both the copy constructor and the
/// default assignment operator.
///
/////////////////////////////////////////////////////////////////////////////////////////////////
class UUncopiable
{
public:
    UUncopiable()=default;
private:
    UUncopiable(const UUncopiable&)=delete;
    UUncopiable& operator=(const UUncopiable&)=delete;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// add support for intrusive pointers
///
/// Intrusive pointers (intrusive_ptr template) provide a reference counting
/// mechanism, keeping the count inside the object. This class provides the
/// data members and functions to make it work.
///
/// If it is not possible to inherit from this class, reference counting can
/// still be used, using shared_ptr instead
///
/// \todo Make the mutex a template parameter
///
/////////////////////////////////////////////////////////////////////////////////////////////////
class UShared
{
protected:
    ///default constructor
    ///
    ///Initializes the reference count to 0, so if the object never gets assigned to an intrusive pointer,
    ///it is valid to delete it. This allows for shared objects to be constructed in the stack, as long
    ///as they never get assigned to an \ref intrusive_ptr.
    UShared()
        :   mDeleteMutex(nullptr)
        ,   mRefCount(0)
    {}
    
    UShared(const UShared&)=delete;
    UShared &operator=(const UShared&)=delete;

    ///destructor
    ///
    ///\throw XInvalid if the reference count is not zero.
    virtual ~UShared()
    {
        if(mRefCount)
            throw XInvalid(I18N("explicitly deleting shared object"));
    }

protected:
    ///sets the delete mutex
    void SetDeleteMutex(mutex *pMutex)
    {
        mDeleteMutex=pMutex;
    }

private:
    ///safely deletes objects which have a mutex
    void IntrusiveDelete();
    
    ///adds one reference to the object
    void IntrusiveAddRef()
    {
        ++mRefCount;
    }
    
    ///removes one reference from the object
    ///
    ///It destroys the object when the reference count reaches zero.
    void IntrusiveRemoveRef()
    {
        if(mRefCount>1)
        {
            --mRefCount;
        }
        else
        {
            if(mDeleteMutex)
            {
                IntrusiveDelete();
            }
            else
            {
                --mRefCount;
                delete this;
            }
        }
    }

    mutex *mDeleteMutex;
    int mRefCount;

    template<typename tType>
    friend class intrusive_ptr;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// unused type
///
/// Can be used in some contexts when a template parameter is not used.
///
/// It is an empty class which can be default constructed and assigned from any type
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct UUnused
{
    UUnused()=default;
    UUnused(const UUnused&)=default;

    template<typename tT>
    UUnused(const tT&)
    {}

    template<typename tT>
    UUnused& operator=(const tT&)
    {
        return *this;
    }

    UUnused& operator=(const UUnused&)=default;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// unknown type
///
/// Can be used in some contexts when a template parameter is unknown.
///
/// It is an empty class for which objects can't be created
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct UUnknown
{
    UUnknown()=delete;
};

///\name Names
//@{

DECLARE_NAME(SameAlloc)
DECLARE_NAME(Clone)
DECLARE_NAME(Steal)
DECLARE_NAME(Share)

template<typename tWhat,typename... tParams>
inline auto clone(tWhat &&pWhat,tParams&&... pParams) -> 
            decltype(ODecay<tWhat>(nClone,std::forward<tWhat>(pWhat),std::forward<tParams>(pParams)...))
{
    return {nClone,std::forward<tWhat>(pWhat),std::forward<tParams>(pParams)...};
}

template<typename tWhat,typename... tParams>
inline auto share(tWhat &&pWhat,tParams&&... pParams) -> 
            decltype(ODecay<tWhat>(nShare,std::forward<tWhat>(pWhat),std::forward<tParams>(pParams)...))
{
    return {nShare,std::forward<tWhat>(pWhat),std::forward<tParams>(pParams)...};
}

//@}

///@}

/*namespace buola*/ }

#endif
